package htmlog;

#
# Copyright (c) 2002-2007 Eric Wallengren
# This file is part of the Continuous Automated Build and Integration
# Environment (CABIE)
#
# CABIE is distributed under the terms of the GNU General Public
# License version 2 or any later version.  See the file COPYING for copying
# permission or http://www.gnu.org.
#
# THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED OR
# IMPLIED, without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  ANY USE IS AT YOUR OWN RISK.
#
# Permission to modify the code and to distribute modified code is granted,
# provided the above notices are retained, and a notice that the code was
# modified is included with the above copyright notice.
#

BEGIN {
    push @INC, "lib";
    push @INC, "../lib";
    push @INC, "../../lib";
}

use Exporter;
use Getopt::Long;
use File::Basename;
use English;
use Sys::Hostname;
use IO::Handle;
use common;

my $hostname    = hostname();
$hostname       =~ s/\.[a-zA-Z0-9]+//g;

my $configname  = $hostname;
$configname     =~ s/-//g;

require "$configname.pm";

my $config = new $configname;

my $logo   = $config->LOGOICON;

@ISA     = qw/ Exporter AutoLoader /;
@EXPORT  = qw/ _genHtmlBuildLog /;

# use strict;

# ------------------------------------------------------------------
my( @whenlist );
my( $output ) = "";
my( $num_sections ) = 0;
my( $num_warnings ) = 0;
my( $num_errors ) = 0;
my( @error_list );
my( %section_link );
my( %warn_link );
my( %error_link );
my( $module_order );
my( %elapsed_time );
my( %total_time );
my( %products );
my( %ignored );
my( $image_platform );
my( $image_product );
my( @HTML );

# ------------------------------------------------------------------
# Load the list of error strings from a file.
#
sub loadErrorList {

    my $buildname = shift;

    my $error_file;

    my $me = (caller(0))[3];

    $error_file = $config->BSR."/config/$buildname.errors";

    open( BERRORFILE, "<$error_file" )
        or die "Can't open $error_file";

    @error_list = <BERRORFILE>;

    close( BERRORFILE );

    chomp @error_list;

}

# ------------------------------------------------------------------
# Return 1 if an error string is detected in the input line.
#
sub hasError {

    my( $line ) = @_;
    my( $err );

    my $me = (caller(0))[3];

    foreach $err ( @error_list ) {
        if ( index( $line, $err ) > -1 ) {
            return 1;
        }
    }

    return 0;

}

# ------------------------------------------------------------------
# Escape HTML characters.  "<" -> &lt; ">" -> &gt; "&" -> &amp;
sub esc {

    my( $text ) = @_;

    my $me = (caller(0))[3];

    $text =~ s/&/&amp;/g;
    $text =~ s/</&lt;/g;
    $text =~ s/>/&gt;/g;

    return $text;

}

# ------------------------------------------------------------------
sub processLog {

    my @log = @_;

    my( $in_header ) = 0;
    my( $when );
    my( %seen );
    my( %seen_product );
    my( $who );
    my( @contents );
    my( $line );

    my $me = (caller(0))[3];

    foreach $entry (@log) {

        if ( $entry =~ /^\s*(\[echo\])?\s*#-----------------/ ) {

            if ( ! $in_header ) {

                $output = $output .
                         "</pre>\n" .
                         "<h4 class='section'>\n" .
                         "<pre>\n" .
                         esc( $entry );

                $in_header = 1;

            } else {

                $in_header = 0;

                $output = $output .
                         esc( $entry ) .
                         "</pre>\n" .
                         "</h4>\n" .
                         "<pre>\n";

            }

        } elsif ( $entry =~ /^\s*\[create-distribution\]\s*creating.*image/ ) {

            my( @words ) = split( / /, $entry );

            $image_platform = $words[ $#words - 2 ] eq "creating"
                            ? "all"
                            : $words[ $#words - 2 ];

            $image_product = $words[ $#words - 1 ];

            $num_sections++;

            $output = $output .
                     "</pre>\n" .
                     "<h4 class='section'>\n" .
                     "<a name='section_$num_sections'><pre>$entry</pre></a>\n" .
                     "</h4>\n" .
                     "<pre>\n";

            $who = "$image_product-$image_platform";
            $section_link{ $who, "image" } = $num_sections;

            if ( ! $seen_product{ $who } ) {
                $seen_product{ $who } = 1;
                $products{ $image_product } .= " " . $image_platform;
            }

        } elsif ( $entry =~ /^\s*\[create-distribution\]\s*elapsed staging time:/ ) {

            my( @words ) = split( / /, $entry );
            my( $secs ) = $words[ $#words - 1 ];
            $output = $output . esc( $entry );
            $elapsed_time{ $who, "image" } = $secs;
            $total_time{ "image" } += $secs;

        } elsif ( $entry =~ /^\s*\[create-distribution\]\s*elapsed iso time:/ ) {

            my( @words ) = split( / /, $entry );
            my( $secs ) = $words[ $#words - 1 ];
            $output = $output . esc( $entry );
            $elapsed_time{ $who, "iso" } = $secs;
            $total_time{ "iso" } += $secs;

        } elsif ( $in_header eq 1 ) {

            chomp $entry;

            my( $ignore );
            my( @words ) = split( / /, $entry );

            $num_sections++;
            $output = $output . "<a name='section_$num_sections'>" . 
                      esc( $entry ) .
                                "</a>\n";

            $ignore = $words[ $#words - 2 ] eq "Ignore";

            if ( $words[ $#words ] eq "module" ) {
                $when = "build";
            } elsif ( $words[ $#words ] eq "package" ) {
                $when = "package";
            } else {
                $when = "?";
            }

            if ( $when ne "?" ) {
                my( $module ) = $words[ $#words - 1 ];

                $who = $module;
                $section_link{ $who, $when } = $num_sections;
                $ignored{ $who, $when } = $ignore;

                if ( ! $seen{ $module } ) {
                    $seen{ $module } = 1;
                    $module_order = "$module_order $module";
                }
            }

        } else {

            if ( hasError( $entry ) ) {

                $num_errors++;
                chomp $entry;
                $output = $output .
                          "</pre>\n" .
                          "<pre class='error'>\n" .
                          "\n" .
                          "<a name='error_$num_errors'>" . esc( $entry ) .
                          "</a>\n" .
                          "\n" .
                          "</pre>\n" .
                          "<pre>\n";

                $error_link{ $who, $when } .= " $num_errors";

            } elsif ( $entry =~ /warning/i ) {

                $num_warnings++;
                chomp $entry;
                $output = $output .
                          "</pre>\n" .
                          "<pre class='warn'>\n" .
                          "\n" .
                          "<a name='warning_$num_warnings'>" . esc( $entry ) .
                          "</a>\n" .
                          "\n" .
                          "</pre>\n" .
                          "<pre>\n";

                $warn_link{ $who, $when } .= " $num_warnings";

            } elsif ( $entry =~ /^\s*(\[echo\])?\s*# .* time: [0-9.:]+ sec/ ) {

                my( @words ) = split( / /, $entry );
                my( $secs ) = $words[ $#words - 1 ];
                my( @t ) = split( /:/, $secs );

                if ( $#t > 0 ) {
                    $secs = $t[ 0 ] * 60 + $t[ 1 ];
                }

                $output = $output . esc( $entry );

                $elapsed_time{ $who, $when } = $secs;

                $total_time{ $when } += $secs;

            } else {

                $output = $output . esc( $entry );

            }

        }

    }

}

# ------------------------------------------------------------------
sub outputHtml {

    my $refresh = shift;

    my @data = ();

    my $entry;

    my $me = (caller(0))[3];
    push @HTML,  "<html>\n";
    push @HTML,  "<head>\n";

    if ( $refresh > 0 ) {
        push @HTML,  "    <meta http-equiv='refresh' content='$refresh'/>\n";
    }

    push @HTML,  "    <style>\n";
    push @HTML,  "        th.title { font-size: 125%; background: #ccccff; }\n";
    push @HTML,  "        td { padding-left: 4; padding-right: 4; }\n";
    push @HTML,  "\n";
    push @HTML,  "        .working { background: #ffff99; }\n";
    push @HTML,  "        .section { background: #ccccff; }\n";
    push @HTML,  "        .success { background: #b9ffb9; }\n";
    push @HTML,  "        .error   { background: #ff9999; }\n";
    push @HTML,  "        .warn    { background: #ffcc99; }\n";
    push @HTML,  "\n";
    push @HTML,  "        .time    { text-align: right; background: #ddddff; ".
                 "font-family: monospace; }\n";
    push @HTML,  "        .name    { background: #ddddff; }\n";

    push @HTML,  "        ul.nav li { display: inline;\n";
    push @HTML,  "                    overflow: hidden;\n";
    push @HTML,  "                    list-style-type: none;\n";
    push @HTML,  "                  }\n";

    push @HTML,  "    </style>\n";
    push @HTML,  "</head>\n";
    push @HTML,  "<body>\n";
    push @HTML,  "<a name='top'/>";
    push @HTML,  "<br>";
    push @HTML,  "<hr>";

    outputNavigation();

    outputModuleTable();
    push @HTML,  "<p/>\n";
    outputCdTable();

    push @HTML,  "<pre>\n";
    push @HTML,  $output;
    push @HTML,  "</pre>\n";

    outputNavigation();

    push @HTML,  "<a name='bottom'/>";
    push @HTML,  "</body>\n";

}

# ------------------------------------------------------------------
sub outputNavigation {

    my $me = (caller(0))[3];

    push @HTML,  "<ul class='nav'>\n";
    push @HTML,  "    <li>Go to:</li>";
    push @HTML,  "    <li><a href='#top'>[top]</a></li>\n";
    push @HTML,  "    <li><a href='#bottom'>[bottom]</a></li>\n";
    push @HTML,  "</ul>\n";

}

# ------------------------------------------------------------------
sub outputModuleTable {

    my( $when );
    my( $module );

    my @data = ();
    my @tmpmods = ();

    my $entry;

    my $me = (caller(0))[3];

    push @HTML,  "<table>\n";

    push @HTML,  "<thead>\n";
    push @HTML,  "<tr>\n";
    push @HTML,  "    <th class='title'>module</th>";

    foreach $when ( @whenlist ) {
        push @HTML,  "    <th/>\n";
        push @HTML,  "    <th class='title' colspan='2'>$when</th>\n";
    }
    push @HTML,  "</tr>\n";
    push @HTML,  "</thead>\n";

    $module_order =~ s/^ //g;
    $module_order =~ s/ $//g;

    @tmpmods = split(/ +/, $module_order);

    foreach $module (@tmpmods) {

        push @HTML,  "<tr>\n";

        push @HTML,  "    <td class='name'>$module</td>\n";

        foreach $when ( @whenlist ) {

            my $timeCell;
            my( $status );

            if ( $elapsed_time{ $module, $when } ) {
                $status = "success";
            } else {
                $status = $ignored{ $module, $when } ? "n/a" : "working";
            }

            push @HTML,  "    <td/>";

            outputStatusCell( $module, $when, $status );

            outputTimeCell( $module, $when );

        }

        push @HTML,  "</tr>\n";
    }

    push @HTML,  "<tr>\n";
    push @HTML,  "    <td class='name'> <b> total </b> </td>\n";
    push @HTML,  "    <td/>\n";
    push @HTML,  "    <td/>\n";
    push @HTML,  "    <td class='time'>";
    push @HTML,       "<b>" . secsToTime( $total_time{ "build" } ) . "</b>";
    push @HTML,  "    </td>\n";
    push @HTML,  "    <td/>\n";
    push @HTML,  "    <td/>\n";
    push @HTML,  "    <td class='time'>";
    push @HTML,       "<b>" . secsToTime( $total_time{ "package" } ) . "</b>";
    push @HTML,  "    </td>\n";
    push @HTML,  "</tr>\n";
    push @HTML,  "</table>\n";

}

# ------------------------------------------------------------------
sub outputCdTable {

    my( $when );
    my( $key );
    my( $platform );

    my $entry;

    my $me = (caller(0))[3];

    if ( %products ) {

        push @HTML,  "<table>\n";

        push @HTML,  "<thead>\n";
        push @HTML,  "<tr>\n";
        push @HTML,  "    <th class='title'>product</th>";
        push @HTML,  "    <th class='title'>platform</th>";
        push @HTML,  "    <th/>\n";
        push @HTML,  "    <th class='title'>status</th>\n";
        push @HTML,  "    <th class='title'>image</th>\n";
        push @HTML,  "    <th class='title'>iso</th>\n";
        push @HTML,  "</tr>\n";
        push @HTML,  "</thead>\n";

        for $key ( sort ( keys %products ) ) {

            my( @platforms ) = split( / /, $products{ $key } );
            my( $first ) = 1;
            my( $span ) = $#platforms + 1;

            push @HTML,  "    <tr/>\n";
            push @HTML,  "    <tr>\n";
            push @HTML,  "        <td class='name' rowspan='$span'>$key</td>\n";

            foreach $platform ( sort @platforms ) {

                my $timeCell;

                my( $who ) = "$key-$platform";

                if ( ! $first ) {

                    push @HTML,  "    <tr>\n";

                }

                push @HTML,  "        <td class='name'>$platform</td>\n";

                push @HTML,  "    <td/>";
                outputStatusCell( $who, "image", "success" );
                outputTimeCell( $who, "image" );

                outputTimeCell( $who, "iso" );

                push @HTML,  "    </tr>\n";

                $first = 0;

            }

        }

        push @HTML,  "<tr>\n";
        push @HTML,  "    <td class='name'> <b> total </b> </td>\n";
    
        push @HTML,  "    <td/>\n";
        push @HTML,  "    <td/>\n";
        push @HTML,  "    <td/>\n";
    
        push @HTML,  "    <td class='time'>";
        push @HTML,       "<b>" . secsToTime( $total_time{ "image" } ) . "</b>";
        push @HTML,  "    </td>\n";
    
        push @HTML,  "    <td class='time'>";
        push @HTML,       "<b>" . secsToTime( $total_time{ "iso" } ) . "</b>";
        push @HTML,  "    </td>\n";
    
        push @HTML,  "</tr>\n";
    
        push @HTML,  "</table>\n";

    }

}

# ------------------------------------------------------------------
sub outputStatusCell {

    my( $who, $when, $status ) = @_;

    my( $section ) = $section_link{ $who, $when };
    my( $warn )    = $warn_link{    $who, $when };
    my( $error )   = $error_link{   $who, $when };
    my( $num );

    my $me = (caller(0))[3];

    # Add all error links

    if ( $error ) {

        push @HTML,  "    <td class='error'>";
        push @HTML,  "\n        <a href='#section_$section'>errors:</a>\n";

        foreach $num ( split( / /, $error ) ) {
            push @HTML,  "       &nbsp;<a href='#error_$num'>$num</a>\n";
        }

        push @HTML,  "    ";

    }

    # Add all warning links

    if ( $warn ) {

        if ( $error ) {
            push @HTML,  "<br/>\n";
        } else {
            push @HTML,  "    <td class='warn'>";
        }

        push @HTML,  "\n        <a href='#section_$section'>warnings:</a>\n";

        foreach $num ( split( / /, $warn ) ) {
            push @HTML,  "       &nbsp;<a href='#warning_$num'>$num</a>\n";
        }

        push @HTML,  "    ";

    }

    # If no error or warnings, add section link if applicable

    if ( ! $error && ! $warn ) {

        if ( $section ) {

            if ( $status eq "working" ) {
                push @HTML,  "    <td class='working'>";
            } else {
                push @HTML,  "    <td class='success'>";
            }

            push @HTML,  "<a href='#section_$section'>$status</a>";

        } else {

            push @HTML,  "    <td class='success'>";
            push @HTML,  "        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .
                               "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";

        }

    }

    push @HTML,  "</td>\n";

}

# ------------------------------------------------------------------
sub outputTimeCell {

    my( $who, $when ) = @_;

    my $me = (caller(0))[3];

    my( $time ) = secsToTime( $elapsed_time{ $who, $when } );

    if ( ! $time ) {
        $time = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
    }

    push @HTML,  "    <td class='time'>$time</td>\n";

}

sub _genHtmlBuildLog {

    my $buildname = shift;
    my @in        = @_;

    my $me = (caller(0))[3];

    @whenlist = ( "build", "package" );
    $output = "";
    $num_sections = 0;
    $num_warnings = 0;
    $num_errors = 0;
    @error_list = (); 
    %section_link = ();
    %warn_link = ();
    %error_link = ();
    $module_order = ();
    %elapsed_time = ();
    %total_time = ();
    %products = ();
    %ignored = ();
    $image_platform = "";
    $image_product = "";
    @HTML = ();

    my @out       = ();

    loadErrorList($buildname);
    processLog(@in);
    outputHtml();

    return @HTML;

}

1;
